﻿using System;
using System.Data.SqlClient;
using DataCenter.Interface;
using DataCenter.Interface.Model;
using DataCenter.Share;
using System.Data;

namespace DataCenter.Component.Processor
{
    public class InterpolateProcessor : IProcessor
    {
        public InterpolateProcessor(SqlCommand command, InterpolateProcessorEntity entity)
        {
            this._Command = command;
            this._Entity = entity;
        }
        private SqlCommand _Command;
        private InterpolateProcessorEntity _Entity;        

        #region IComponent Members

        public bool Initialize()
        {
            return true;
        }

        #endregion

        #region IProcessor Members

        public bool Execute(ObjectEntity objectEntity, DateTime time0, DateTime time1)
        {
            DateTime curTime = time0.Date;
            while (curTime <= time1)
            {
                // 读取当前记录
                BinaryRecord record = GetRecord(objectEntity, curTime, true, true);

                // 判断当前记录是否需要进行插值处理
                // 如果InterpolateTime为23:59分，则不需要进行插值；                
                if (!Utils.IsLastTime(record.InterpolateTime))
                {
                    // 如果time1小于等于InterpolateTime，则不需要进行插值
                    if (time1 > record.InterpolateTime)
                    {
                        Binary binary = record.BinaryValue;
                        binary.BeginEdit();

                        // 准备当前记录的插值范围
                        DateTime startTime = record.ValueTime;
                        DateTime endTime = Utils.GetLastTime(record.ValueTime);
                        if (record.InterpolateTime != Utils.EMPTY_TIME)
                        {
                            // 如果InterpolateTime不等于1900-1-1，
                            // 则用InterpolateTime前的第一个真实数据所在时间作为开始时间
                            DateTime time = binary.GetLastTime(Utils.REAL_FLAG, record.ValueTime, record.InterpolateTime);
                            if (time != Utils.EMPTY_TIME)
                                startTime = time;
                        }
                        // 如果当前记录日期为今天，则插值结束到当天时间
                        if (record.ValueTime == Utils.TODAY)
                            endTime = Utils.NOW;

                        // 将当前记录中，插值时间范围内非真实数据清除
                        if (!record.NewRecord && binary.RecordCount > 0)
                            binary.SetFlag(startTime, endTime, Utils.UNREAL_FLAG, Binary.EMPTY_FLAG);

                        // 如果ForwardSearchMinutes大于0，则需要向前寻找数据；
                        if (_Entity.ForwardSearchMinutes > 0)
                        {
                            // 计算向前寻找时间，当前记录0:00向前寻找ForwardSearchMinutes分钟
                            DateTime forwardSearchTime = record.ValueTime.AddMinutes(-1 * _Entity.ForwardSearchMinutes);

                            // 需要读取前一天记录，
                            // 用前一天记录的最后一个真实数据和当前记录的第一个真实数据进行插值
                            BinaryRecord prevRecord = GetRecord(objectEntity, forwardSearchTime, true, false);
                            if (prevRecord != null)
                            {
                                Binary prevBinary = prevRecord.BinaryValue;
                                prevBinary.BeginEdit();

                                DateTime prevTime0 = forwardSearchTime;
                                DateTime prevTime1 = Utils.GetLastTime(forwardSearchTime);

                                TimePoint lastPoint = prevBinary.GetLastPoint(Utils.REAL_FLAG, prevTime0, Utils.GetLastTime(prevTime1));
                                if (lastPoint != null)
                                {
                                    prevTime0 = lastPoint.Time;

                                    if (_Entity.InterpolateType == InterpolateTypes.Linear)
                                    {
                                        TimePoint firstPoint = binary.GetFirstPoint(Utils.REAL_FLAG);
                                        if (firstPoint != null)
                                        {
                                            // 如果当前记录的插值时间小于第一个真实数据时间，则需要进行头数据计算
                                            if (record.InterpolateTime < firstPoint.Time)
                                            {
                                                float factor = GetLinearFactor(prevTime0, lastPoint.Value0, firstPoint.Time, firstPoint.Value0);

                                                // 如果当前数据第一个真实数据为0:00，
                                                // 则不计算当前记录透数据
                                                if (!Utils.IsFirstTime(firstPoint.Time))
                                                {
                                                    // 将插值数据设置为当前记录的0:00数据
                                                    float headValue = GetLinearValue(factor, prevTime0, lastPoint.Value0, startTime);
                                                    binary.SetValue(startTime, Utils.UNREAL_FLAG, 0, headValue);
                                                }

                                                // 如果前一天的插值时间为23:59，并且前一天的最后一个真实数据不是23:59，
                                                // 则需要对前一天记录的尾部进行插值
                                                if (!prevRecord.NewRecord &&
                                                    !Utils.IsLastTime(lastPoint.Time) &&
                                                    Utils.IsLastTime(prevRecord.InterpolateTime))
                                                {
                                                    float tailValue = GetLinearValue(factor, prevTime0, lastPoint.Value0, prevTime1);
                                                    prevBinary.SetFlag(prevTime0, prevTime1, Utils.UNREAL_FLAG, Binary.EMPTY_FLAG);
                                                    prevBinary.SetValue(prevTime1, Utils.UNREAL_FLAG, 0, tailValue);
                                                    prevBinary.LinearInterpolate(0, Utils.UNREAL_FLAG, prevTime0, prevTime1);
                                                    prevRecord.UpdateRecord(_Command, prevTime0, prevTime1);
                                                }
                                            }
                                        }
                                        else
                                        {
                                            // 如果在当前记录中找不到有效数据，则直接用前一天的真实数据
                                            binary.SetValue(startTime, Utils.UNREAL_FLAG, 0, lastPoint.Value0);
                                        }
                                    }
                                    else
                                    {
                                        // 如果为状态插值，则不需要对前一天数据进行处理
                                        binary.SetValue(startTime, Utils.UNREAL_FLAG, 0, lastPoint.Value0);
                                    }
                                }
                                else
                                {
                                    // 如果找不到前一天数据中，ForwardSearchMinutes时间内的真实数据，则使用默认值
                                    float defaultValue = GetDefaultValue(objectEntity, binary, 0);
                                    binary.SetValue(startTime, Utils.UNREAL_FLAG, 0, defaultValue);
                                }
                            }
                            else
                            {
                                // 如果找不到前一天数据，则使用默认值
                                float defaultValue = GetDefaultValue(objectEntity, binary, 0);
                                binary.SetValue(startTime, Utils.UNREAL_FLAG, 0, defaultValue);
                            }
                        }
                        else
                        {
                            // 如果不需要关心前一天数据，则查看第一个真实数据是否为0:00，
                            // 如果不是，0:00用默认值填充
                            TimePoint firstPoint = binary.GetFirstPoint(Utils.REAL_FLAG);
                            if (firstPoint == null || !Utils.IsFirstTime(firstPoint.Time))
                            {
                                float defaultValue = GetDefaultValue(objectEntity, binary, 0);
                                binary.SetValue(startTime, Utils.UNREAL_FLAG, 0, defaultValue);
                            }
                        }

                        // 如果BackwardSearchMinutes大于0，则需要向后寻找数据；
                        if (_Entity.BackwardSearchMinutes > 0)
                        {
                            // 计算向后寻找时间，后一天数据0:00向后寻找BackwardSearchMinutes分钟
                            DateTime backwardSearchTime = record.ValueTime.AddDays(1).AddMinutes(_Entity.BackwardSearchMinutes);

                            // 需要读取后一天记录，
                            // 用当前记录的最后一个真实数据和后一天记录的第一个真实数据进行插值
                            BinaryRecord nextRecord = GetRecord(objectEntity, backwardSearchTime, true, false);
                            if (nextRecord != null)
                            {
                                Binary nextBinary = nextRecord.BinaryValue;
                                nextBinary.BeginEdit();

                                DateTime nextTime0 = backwardSearchTime.Date;
                                DateTime nextTime1 = backwardSearchTime;

                                TimePoint firstPoint = nextBinary.GetFirstPoint(Utils.REAL_FLAG, nextTime0, nextTime1);
                                if (firstPoint != null)
                                {
                                    nextTime1 = firstPoint.Time;

                                    if (_Entity.InterpolateType == InterpolateTypes.Linear)
                                    {
                                        TimePoint lastPoint = binary.GetLastPoint(Utils.REAL_FLAG);
                                        if (lastPoint != null)
                                        {
                                            float factor = GetLinearFactor(lastPoint.Time, lastPoint.Value0, nextTime1, firstPoint.Value0);

                                            // 如果当前记录的最后一个真实数据为23:59，则不计算当前记录的尾数据
                                            if (!Utils.IsLastTime(lastPoint.Time))
                                            {
                                                // 将插值数据设置为当前记录23:59数据
                                                float tailValue = GetLinearValue(factor, lastPoint.Time, lastPoint.Value0, endTime);
                                                binary.SetValue(endTime, Utils.UNREAL_FLAG, 0, tailValue);
                                            }

                                            // 如果后一天的插值时间大于等于后一天的第一个真实数据时间，
                                            // 并且后一天的第一个真实数据不是0:00，则需要对后一天的头数据进行插值
                                            if (!nextRecord.NewRecord &&
                                                !Utils.IsFirstTime(firstPoint.Time) &&
                                                nextRecord.InterpolateTime >= firstPoint.Time)
                                            {
                                                float headValue = GetLinearValue(factor, lastPoint.Time, lastPoint.Value0, nextTime0);
                                                nextBinary.SetFlag(nextTime0, nextTime1, Utils.UNREAL_FLAG, Binary.EMPTY_FLAG);
                                                nextBinary.SetValue(nextTime0, Utils.UNREAL_FLAG, 0, headValue);
                                                nextBinary.LinearInterpolate(0, Utils.UNREAL_FLAG, nextTime0, nextTime1);
                                                nextRecord.UpdateRecord(_Command, nextTime0, nextTime1);
                                            }
                                        }
                                        else
                                        {
                                            // 如果在当前记录中找不到有效数据，则直接用后一天的真实数据
                                            binary.SetValue(endTime, Utils.UNREAL_FLAG, 0, firstPoint.Value0);

                                            // 如果后一天的插值时间大于等于后一天的第一个真实数据时间，
                                            // 并且后一天的第一个真实数据不是0:00，则需要对后一天的头数据进行插值
                                            if (nextRecord.InterpolateTime >= firstPoint.Time &&
                                                !Utils.IsFirstTime(firstPoint.Time))
                                            {
                                                nextBinary.SetFlag(nextTime0, nextTime1, Utils.UNREAL_FLAG, Binary.EMPTY_FLAG);
                                                nextBinary.SetValue(nextTime0, Utils.UNREAL_FLAG, 0, firstPoint.Value0);
                                                nextBinary.LinearInterpolate(0, Utils.UNREAL_FLAG, nextTime0, nextTime1);
                                                nextRecord.UpdateRecord(_Command, nextTime0, nextTime1);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        // 如果为状态插值，不需要对后一天数据进行处理
                                        binary.SetValue(endTime, Utils.UNREAL_FLAG, 0, firstPoint.Value0);
                                    }
                                }
                                else
                                {
                                    // 如果找不到后一天数据中，BackwardSearchMinutes时间内的真实数据，则使用默认值
                                    float defaultValue = GetDefaultValue(objectEntity, binary, 1);
                                    binary.SetValue(endTime, Utils.UNREAL_FLAG, 0, defaultValue);
                                }
                            }
                            else
                            {
                                // 如果找不到后一天数据，则使用默认值
                                float defaultValue = GetDefaultValue(objectEntity, binary, 1);
                                binary.SetValue(endTime, Utils.UNREAL_FLAG, 0, defaultValue);
                            }
                        }
                        else
                        {
                            // 如果不需要关心后一天的数据，则查看最后一个真实数据时间是否为23:59
                            // 如果不是，23:59数据使用默认值填充
                            TimePoint lastPoint = binary.GetLastPoint(Utils.REAL_FLAG);
                            if (lastPoint == null || !Utils.IsLastTime(lastPoint.Time))
                            {
                                float defaultValue = GetDefaultValue(objectEntity, binary, 1);
                                binary.SetValue(endTime, Utils.UNREAL_FLAG, 0, defaultValue);
                            }
                        }

                        if (_Entity.InterpolateType == InterpolateTypes.Linear)
                        {
                            binary.LinearInterpolate(0, Utils.UNREAL_FLAG, startTime, endTime);
                        }
                        else
                        {
                            binary.SwitchInterpolate(0, Utils.UNREAL_FLAG, startTime, endTime);
                        }

                        record.UpdateInterpolateTime();
                        //record.IsSubmit = true;
                        binary.EndEdit();
                    }
                }

                curTime = curTime.AddDays(1);
            }

            return true;
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            
        }

        #endregion

        private BinaryRecord GetRecord(ObjectEntity objectEntity, DateTime valueTime, bool isLoadFromDb, bool isCreate)
        {
            // 读取当前记录，如果内存中记录不存在，则从数据库中读取；
            // 如果数据库中也不存在，则创建该记录，并缓存到内存中
            BinaryRecord record = objectEntity.GetRecord(valueTime);
            if (record == null && isLoadFromDb)
                record = objectEntity.SearchRecord(_Command, valueTime, true);
            if (record == null && isCreate)
            {
                record = new BinaryRecord();
                record.ObjectId = objectEntity.ObjectId;
                record.ValueType = objectEntity.ObjectType;
                record.ValueTime = valueTime;
                record.BinaryValue = new DayBinary();
                objectEntity.AddRecord(record);
            }
            return record;
        }

        private float GetLinearFactor(DateTime time0, float value0, DateTime time1, float value1)
        {
            return (value1 - value0) / (time1.Ticks - time0.Ticks);
        }

        private float GetLinearValue(float factor, DateTime time0, float value0, DateTime time)
        {
            return (time.Ticks - time0.Ticks) * factor + value0;
        }

        private float GetDefaultValue(ObjectEntity objectEntity, Binary binary, int headOrTail)
        {
            if (_Entity.IsRealValueAsDefault)
            {
                if (binary != null)
                {
                    TimePoint point = null;
                    if (headOrTail == 0)
                    {
                        point = binary.GetFirstPoint(Utils.REAL_FLAG);
                    }
                    else
                    {
                        point = binary.GetLastPoint(Utils.REAL_FLAG);
                    }
                    if (point != null)
                        return point.Value0;
                }
            }
            return objectEntity.DefaultValue;
        }
    }
}
